/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package studio.raptor.ddal.core.engine;

import studio.raptor.ddal.common.sql.SqlUtil;
import studio.raptor.ddal.core.constants.FlowType;
import studio.raptor.ddal.core.engine.plan.PlanNodeChain;
import studio.raptor.ddal.core.engine.processdefinition.SqlProcessDefinition;
import studio.raptor.ddal.core.parser.Parser;
import studio.raptor.ddal.core.parser.result.ParseResult;

/**
 * Process engine works for jdbc and proxy mode.
 *
 * @author Sam
 * @since 3.0.0
 */
public final class ProcessEngine {


  private ProcessContext processContext;

  /**
   * 关系型数据库处理流程定义。
   */
  private static SqlProcessDefinition processDef = new SqlProcessDefinition();

  ProcessEngine(ProcessContext processContext) {
    this.processContext = processContext;
  }

  public void process(FlowType flowType) {
    switch (flowType) {
      case DML:
        parseSql();
        // 对于已存在执行流程实例的SQL，直接使用流程实例执行
        PlanNodeChain planInstance =
            MemoryObjectsBasedOnSQL.getMemoryPlan(processContext.getTrimmedHintSql());
        if (null != planInstance) {
          processContext.setSolidifyPlan(false);
          planInstance.runFromStart(processContext);
        } else {
          processContext.setSolidifyPlan(true);
          processContext.setPlanInstance(PlanNodeChain.createInstance());
          PlanNodeChain.createTemplate().runFromStart(processContext);
          MemoryObjectsBasedOnSQL.memorizePlan(
              processContext.getTrimmedHintSql(), processContext.getPlanInstance());
        }
        break;
      case COMMIT:
        processDef.commitFlow(getProcessContext());
        break;
      case ROLLBACK:
        processDef.rollbackFlow(getProcessContext());
        break;
      default:
        break;
    }
  }

  /**
   * SQL解析以及缓存解析结果。解析结果可缓存的前提条件是预编译语句。因为
   * SQL语句的注释会不断地变化，所以缓存解析结果时会先删除SQL语句前部的
   * 注释。
   */
  private void parseSql() {
    String sql = processContext.getOriginSql();
    processContext.setTrimmedHintSql(sql.substring(SqlUtil.findStartOfStatement(sql)));
    ParseResult parsedResult = MemoryObjectsBasedOnSQL
        .getMemoryParseResult(processContext.getTrimmedHintSql());
    if (null == parsedResult) {
      parsedResult = Parser.parse(processContext.getDatabaseType(), sql);
      MemoryObjectsBasedOnSQL.memorizeParseResult(processContext.getTrimmedHintSql(), parsedResult);
    }
    processContext.setParseResult(parsedResult);
  }

  public ProcessContext getProcessContext() {
    return processContext;
  }
}
